package multiNyID.MarkerModel;

/*
 * class MarkerModelNyID
 *
 * bikin kelas untuk menghubungkan antara marker dengan objek
 * objek ------> model
 *          |--> pointer
 *          |--> kumpulan bidak
 * --------------------------------------------------------------------------------
 * Based On NyARToolkit The Java edition ARToolKit class library.
 * modified from class MarkerModel - Andrew Davison [ad@fivedots.coe.psu.ac.th]
 *
 * @author whe_green@yahoo.co.id
 * Analisis Metode Occlusion Based Pada Augmented Reality
 * Studi Kasus Interaksi dengan Objek Virtual Secara Realtime
 * Menggunakan Gerakan Tangan
 *
 * Institut Teknologi Telkom
 * Fakultas Informatika 2007
 * Bandung Indonesia
 *
 * nyargreen.wordpress.com
 */

import multiNyID.Objek.SmoothMatrix;
import javax.media.j3d.*;
import javax.vecmath.*;


public class MarkerModelNyID
{
  private double MARKER_SIZE = 0.12;    // satuan dalam meter
  private int NyID;
  private String modelName;

  private boolean isVisible;
  private TransformGroup moveTg;      // for moving the marker model
  private SmoothMatrix sMat;          // for smoothing the transforms applied to the model

  private GridModel pointer;
  private GridModel bidak;
  private SlideModel slide;

  // details about a model's position and orientation (in degrees)
  private Point3d posInfo = null;
  private Point3d rotsInfo = null;

  private int numTimesLost = 0;   // number of times marker for this model not detected

  private boolean statModel = false; //penanda cuma marker (false) atau punya model (true)

  private Vector3d posTranslasi;

  private ManipulasiModel model;

  public boolean[] lock =  {false,false};

// konstruktor1 --> untuk bikin markermodel dengan model berupa objek j3d
  public MarkerModelNyID(int markerNyID, BranchGroup sceneGroup, double scale, Vector3d p, double ukurMrkr)
    {

      //load model
    this.model = new ManipulasiModel(sceneGroup, scale);
    moveTg = new TransformGroup();
    moveTg.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);  // so this tg can change
//    moveTg.addChild(model.getVisSwitch());
    moveTg.addChild(model.getModelBG());
    
    this.MARKER_SIZE = ukurMrkr;
    this.NyID = markerNyID;

    this.statModel = true;
    this.posTranslasi = p;

    //grid
    this.pointer = new GridModel();
    this.bidak = new GridModel();
    this.slide = new SlideModel();
//    moveTg.addChild(pointer.getGridBG());
    moveTg.addChild(pointer.getVisSwitch());
//    moveTg.addChild(bidak.getGridBG());
    moveTg.addChild(bidak.getVisSwitch());
    moveTg.addChild(slide.getVisSwitch());
    this.isVisible = false;
    sMat = new SmoothMatrix();
  }

// konstruktor2 --> untuk bikin markermodel dengan model format lain
  public MarkerModelNyID(int markerNyID, String modelFnm, double scale, boolean hasCoords, Vector3d p, double ukurMrkr)
    {
    this.MARKER_SIZE = ukurMrkr;
    this.NyID = markerNyID;
    this.modelName = modelFnm.substring(0, modelFnm.lastIndexOf('.'));   // remove filename extension
    this.statModel = true;
    this.posTranslasi = p;

    //load model
    this.model = new ManipulasiModel(modelFnm, scale, hasCoords);
  
    moveTg = new TransformGroup();
    moveTg.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);  // so this tg can change

    moveTg.addChild(model.getModelBG());  
    
    //grid
    this.pointer = new GridModel();
//    moveTg.addChild(pointer.getGridBG());
    moveTg.addChild(pointer.getVisSwitch());

    this.isVisible = false;
    this.sMat = new SmoothMatrix();
  }

// konstruktor3 --> untuk bikin markermodel tanpa model
  public MarkerModelNyID(int markerNyID, Vector3d p)
  {
    NyID = markerNyID;
    posTranslasi = p;
    
    sMat = new SmoothMatrix();
  }  // end of MarkerModel()  

  public double getMarkerWidth()
  { // System.out.println("Width: " + markerInfo.getWidth());
    return MARKER_SIZE;   // markerInfo.getWidth() not valid since requires Java 3D units
  }

  public void moveModel(Matrix4d m4d)
  // detected marker so update model's moveTG
  {
    showModel();
    sMat.add(m4d);
    Matrix4d mat = sMat.get();
//    Matrix4d mat = m4d;

    Transform3D t3d = new Transform3D(mat);

    int flags = t3d.getType();
    if ((flags & Transform3D.AFFINE) == 0)
      System.out.println("Ignoring non-affine transformation");
    else {
      if (getMoveTg() != null)
        getMoveTg().setTransform(t3d);

      // System.out.println("transformation matrix: " + mat);
      calcPosition(mat);
      calcEulerRots(mat);
    }
  }  // end of moveModel()


  private void calcPosition(Matrix4d mat)
  // extract the (x,y,z) position vals stored in the matrix
  {
    // convert to cm and round
//    double x = roundToNumPlaces( mat.getElement(0,3)*100, 5);
//    double y = roundToNumPlaces( mat.getElement(1,3)*100, 5);
//    double z = roundToNumPlaces( mat.getElement(2,3)*100, 5);
    // System.out.println(getNameInfo() + " (" + x + ", " + y + ", " + z + ")");
    double x = ( mat.getElement(0,3)*100);
    double y = ( mat.getElement(1,3)*100);
    double z = ( mat.getElement(2,3)*100);
    posInfo = new Point3d(x, y, z);
  }  // end of reportPosition()


  private double roundToNumPlaces(double val, int numPlaces)
  {
    double power = Math.pow(10, numPlaces);
    long temp = Math.round(val*power);
    return ((double)temp)/power;
  }


  public Point3d getPos()
  {  return posInfo;  }

  private void calcEulerRots(Matrix4d mat)
  /* calculate the Euler rotation angles from the upper 3x3 rotation
     components of the 4x4 transformation matrix. 
     Based on code by Daniel Selman, December 1999
     which is based on pseudo-code in "Matrix and Quaternion FAQ", Q37
       http://www.j3d.org/matrix_faq/matrfaq_latest.html
  */
  {
    rotsInfo = new Point3d();

    rotsInfo.y = -Math.asin(mat.getElement(2,0));
    double c = Math.cos(rotsInfo.y);

    double tRx, tRy, tRz;
    if(Math.abs(rotsInfo.y) > 0.00001) {
      tRx = mat.getElement(2,2)/c;
      tRy = -mat.getElement(2,1)/c;
      rotsInfo.x = Math.atan2(tRy, tRx);

      tRx = mat.getElement(0,0)/c;
      tRy = -mat.getElement(1,0)/c;
      rotsInfo.z = Math.atan2(tRy, tRx);
    }
    else {
      rotsInfo.x  = 0.0;

      tRx = mat.getElement(1,1);
      tRy = mat.getElement(0,1);
      rotsInfo.z = Math.atan2(tRy, tRx);
    }

    rotsInfo.x = -rotsInfo.x;
    rotsInfo.z = -rotsInfo.z;

    // ensure the values are positive by adding 2*PI if necessary...
    if(rotsInfo.x < 0.0)
    rotsInfo.x += 2*Math.PI;

    if(rotsInfo.y < 0.0)
      rotsInfo.y += 2*Math.PI;

    if(rotsInfo.z < 0.0)
      rotsInfo.z += 2*Math.PI;

    // convert to degrees and round
    rotsInfo.x = roundToNumPlaces( Math.toDegrees(rotsInfo.x), 0);
    rotsInfo.y = roundToNumPlaces( Math.toDegrees(rotsInfo.y), 0);
    rotsInfo.z = roundToNumPlaces( Math.toDegrees(rotsInfo.z), 0);

    // System.out.println(getNameInfo() + " rots (" + 
    //         rotsInfo.x + ", " + rotsInfo.y + ", " + rotsInfo.z + ")");
  }  // end of calcEulerRots()


  public Point3d getRots()
  {  return rotsInfo; }

  public void resetNumTimesLost()
  {  numTimesLost = 0;  }

  public void incrNumTimesLost()
  {  numTimesLost++;  }

  public int getNumTimesLost()
  {  return numTimesLost;  }

  public void hideModel()
  {
      if (isStatModel()){
        model.switchOn( Switch.CHILD_NONE );   // make model invisible
//        pointer.( Switch.CHILD_NONE );   // make model invisible
        pointer.setSwitchOn(Switch.CHILD_NONE);
        bidak.setSwitchOn(Switch.CHILD_NONE);

      }
        isVisible = false;
  }
  public void showModel()
  {
        if (isStatModel()){
          model.switchOn( Switch.CHILD_ALL );   // make visible
          pointer.setSwitchOn(Switch.CHILD_ALL);
        bidak.setSwitchOn(Switch.CHILD_ALL);
          }
        isVisible = true;
  }
    public boolean isStatModel() {
        return statModel;
    }

    public Vector3d getPosisi() {
        return posTranslasi;
    }

    public boolean isVisible()
    {  return isVisible; }

    public int getNyID() {
        return NyID;
    }

    public ManipulasiModel getModel() {
        return model;
    }

    public TransformGroup getMoveTg() {
        return moveTg;
    }

    public void tambahPointer(BranchGroup bg, double skala, int posX, int posY){
        
        this.pointer.createModel(bg, skala, posX, posY,Switch.CHILD_NONE);
//        this.pointer.createModel(bg, skala, posX, posY,Switch.CHILD_ALL);
    }

    public void tambahPointer(String modelFnm, double skala,boolean hasOrd, int posX, int posY){
        this.pointer.createModel(modelFnm, skala, hasOrd, posX, posY,Switch.CHILD_NONE);
    }
    public void tambahBidak(BranchGroup bg, double skala, int posX, int posY){
        this.bidak.createModel(bg, skala, posX, posY,Switch.CHILD_ALL);
    }

    public void tambahBidak(String modelFnm, double skala,boolean hasOrd, int posX, int posY){
        this.bidak.createModel(modelFnm, skala, hasOrd, posX, posY,Switch.CHILD_ALL);
    }

    public void tambahSlide(BranchGroup bg, double skala){
        this.slide.createModel(bg, skala);
    }

    public void tambahSlide(String modelFnm, double skala,boolean hasOrd){
        this.slide.createModel(modelFnm, skala, hasOrd);
    }
    public SlideModel getSlide() {
        return slide;
    }

    public GridModel getPointer() {
        return pointer;
    }

    public GridModel getBidak() {
        return bidak;
    }
    
}  // end of MarkerModel class
